﻿#region Copyright Notice
// 
// 
// Copyright (c) 2009-2010 Anindya Chatterjee
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 
// 
// 
#endregion
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Net;
using GCodeIssueTracker.Comments;
using GCodeIssueTracker.Query;
using GCore;

namespace GCodeIssueTracker
{
    public sealed partial class ProjectHostingService
    {
        private readonly HybridDictionary _clientStore = new HybridDictionary();
        private WebClient _client;

        #region GetIssues Async

        /// <summary>
        /// Occurs when an asynchronous request for <see cref="GetIssuesAsync"/> operation completes.
        /// </summary>
        public event GetAllFeedCompletedEventHandler<IssuesFeed, IssuesEntry> GetIssuesCompleted;

        /// <summary>
        /// Gets issues for a project as a <see cref="IssuesFeed"/> object. Max result is 25. This method doesnot block the calling thread.
        /// </summary>
        /// <param name="userState">A user-defined object that is passed to the method invoked when the asynchronous operation completes.</param>
        public void GetIssuesAsync(object userState)
        {
            GUrl = "http://code.google.com/feeds/issues/p/" + ProjectName + "/issues/full";
            GetAllFeedCompleted += ProjectHostingServiceDownloadFeedCompleted;
            GetAllFeedAsync(userState);
        }

        /// <summary>
        /// Cancels a pending asynchronous operation for <see cref="GetIssuesAsync"/>.
        /// </summary>
        /// <param name="userState">A user-defined object to uniquely determine the asynchronous operation.</param>
        public void GetIssuesAsyncCancel(object userState)
        {
            GetAllFeedAsyncCancel(userState);
        }

        private void ProjectHostingServiceDownloadFeedCompleted(object sender,
                                                                GetAllFeedCompletedEventArgs<IssuesFeed, IssuesEntry> e)
        {
            if (GetIssuesCompleted != null)
                GetIssuesCompleted(sender, e);
        }

        #endregion

        #region GetAllIssues Async

        /// <summary>
        /// Occurs when an asynchronous request for <see cref="GetAllIssuesAsync"/> operation completes.
        /// </summary>
        public event GetAllFeedCompletedEventHandler<IssuesFeed, IssuesEntry> GetAllIssuesCompleted;

        /// <summary>
        /// Gets all the issues for a project as a <see cref="IssuesFeed"/> object. This method doesnot block the calling thread.
        /// </summary>
        /// <param name="userState">A user-defined object that is passed to the method invoked when the asynchronous operation completes.</param>
        public void GetAllIssuesAsync(object userState)
        {
            var query = new IssueQuery();
            query.AppendQuery(QueryParamType.MaxResults, Int32.MaxValue.ToString());
            QueryIssueCompleted += ProjectHostingServiceGetAllIssuesCompleted;
            QueryIssueAsync(query, userState);
        }

        /// <summary>
        /// Cancels a pending asynchronous operation for <see cref="GetAllIssuesAsync"/>.
        /// </summary>
        /// <param name="userState">A user-defined object to uniquely determine the asynchronous operation.</param>
        public void GetAllIssuesAsyncCancel(object userState)
        {
            GetAllFeedAsyncCancel(userState);
        }

        private void ProjectHostingServiceGetAllIssuesCompleted(object sender,
                                                                GetAllFeedCompletedEventArgs<IssuesFeed, IssuesEntry> e)
        {
            if (GetAllIssuesCompleted != null)
                GetAllIssuesCompleted(sender, e);
        }

        #endregion

        #region GetAllIssuesSummery Async

        /// <summary>
        /// Occurs when an asynchronous request for <see cref="GetAllIssuesSummeryAsync"/> operation completes.
        /// </summary>
        public event GetAllIssuesSummeryCompletedEventHandler GetAllIssuesSummeryCompleted;

        /// <summary>
        /// Get the summery of all the issues for a project as a <see cref="IssuesFeed"/>. This method doesnot block the calling thread.
        /// </summary>
        /// <param name="userState">A user-defined object that is passed to the method invoked when the asynchronous operation completes.</param>
        /// <remarks>The resultant <see cref="IssuesFeed"/> doesnot contain all the data, but a summery of data.<para>
        /// This method gets a quick summery of all the issues by means of a csv file download.
        /// </para></remarks>
        public void GetAllIssuesSummeryAsync(object userState)
        {
            string url = "http://code.google.com/p/" + ProjectName +
                         "/issues/csv?can=1&q=&colspec=ID+Type+Status+Milestone+Priority+Stars+Owner+Summary";
            var client = new WebClient();
            if (ProxySettings != null)
                client.Proxy = ProxySettings;
            client.Credentials = new NetworkCredential
                                     {
                                         UserName = GUserName,
                                         Password = GPassword
                                     };


            if (userState != null)
                lock (_clientStore.SyncRoot)
                {
                    _clientStore.Add(userState, client);
                }
            else
                _client = client;

            string tempFile = Path.GetTempFileName();
            client.DownloadFileCompleted += DownloadCSVFileCompleted;
            client.DownloadFileAsync(new Uri(url), tempFile,
                                     new DownloadData {Token = userState, DownloadPath = tempFile});
        }

        /// <summary>
        /// Cancels a pending asynchronous operation for <see cref="GetAllIssuesSummeryAsync"/>.
        /// </summary>
        /// <param name="userState">A user-defined object to uniquely determine the asynchronous operation.</param>
        public void GetAllIssuesSummeryAsyncCancel(object userState)
        {
            lock (_clientStore.SyncRoot)
            {
                if (userState == null || !_clientStore.Contains(userState))
                {
                    _client.CancelAsync();
                    return;
                }
                var client = _clientStore[userState] as WebClient;
                if (client != null) client.CancelAsync();
            }
        }

        private void DownloadCSVFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (GetAllIssuesSummeryCompleted != null || !e.Cancelled)
            {
                var downloadData = e.UserState as DownloadData;
                IssuesFeed issueFeed = GetIssueFromCSV(downloadData.DownloadPath);
                File.Delete(downloadData.DownloadPath);
                var eventArgs = new GetAllIssuesSummeryCompletedEventArgs(issueFeed, e.Error, e.Cancelled, e.UserState);
                GetAllIssuesSummeryCompleted(sender, eventArgs);
            }
        }

        #endregion

        #region SubmitNewIssue Async

        /// <summary>
        /// Occurs when an asynchronous request for <see cref="SubmitNewIssueAsync"/> operation completes.
        /// </summary>
        public event SubmitNewEntryCompletedEventHandler<IssuesFeed, IssuesEntry> SubmitNewIssueCompleted;

        /// <summary>
        /// Submits a new <see cref="IssuesEntry"/> into the project.
        /// </summary>
        /// <param name="newEntry">A new issue entry.</param>
        /// <param name="applicationName">Application name invoking the submission.</param>
        /// <param name="userState">A user-defined object that is passed to the method invoked when the asynchronous operation completes.</param>
        public void SubmitNewIssueAsync(IssuesEntry newEntry, string applicationName, object userState)
        {
            if (string.IsNullOrEmpty(applicationName))
                applicationName = "GCodeIssueTracker-1.0";

            GUrl = "http://code.google.com/feeds/issues/p/" + ProjectName + "/issues/full";
            SubmitNewEntryCompleted += ProjectHostingServiceSubmitNewEntryCompleted;
            SubmitNewEntryAsync(newEntry, applicationName, userState);
        }

        /// <summary>
        /// Cancels a pending asynchronous operation for <see cref="SubmitNewIssueAsync"/>.
        /// </summary>
        /// <param name="userState">A user-defined object to uniquely determine the asynchronous operation.</param>
        public void SubmitNewIssueAsyncCancel(object userState)
        {
            SubmitNewEntryAsyncCancel(userState);
        }

        private void ProjectHostingServiceSubmitNewEntryCompleted(object sender,
                                                                  SubmitNewEntryCompletedEventArgs
                                                                      <IssuesFeed, IssuesEntry> e)
        {
            if (SubmitNewIssueCompleted != null)
                SubmitNewIssueCompleted(sender, e);
        }

        #endregion

        #region QueryIssue Async

        /// <summary>
        /// Occurs when an asynchronous request for <see cref="QueryIssueAsync"/> operation completes.
        /// </summary>
        public event GetAllFeedCompletedEventHandler<IssuesFeed, IssuesEntry> QueryIssueCompleted;

        /// <summary>
        /// Queries the project service to get issues depending on certain query parameters. This method doesnot block the calling thread.
        /// </summary>
        /// <param name="query">The data about the query paramteres.</param>
        /// <param name="userState">A user-defined object that is passed to the method invoked when the asynchronous operation completes.</param>
        public void QueryIssueAsync(IGQuery query, object userState)
        {
            GUrl = "http://code.google.com/feeds/issues/p/" + ProjectName + "/issues/full";
            QueryCompleted += ProjectHostingServiceQueryCompleted;
            QueryAsync(query, userState);
        }

        /// <summary>
        /// Cancels a pending asynchronous operation for <see cref="QueryIssueAsync"/>.
        /// </summary>
        /// <param name="userState">A user-defined object to uniquely determine the asynchronous operation.</param>
        public void QueryIssueAsyncCancel(object userState)
        {
            QueryAsyncCancel(userState);
        }

        private void ProjectHostingServiceQueryCompleted(object sender,
                                                         GetAllFeedCompletedEventArgs<IssuesFeed, IssuesEntry> e)
        {
            if (QueryIssueCompleted != null)
                QueryIssueCompleted(sender, e);
        }

        #endregion

        #region GetAllIssueComments Async

        /// <summary>
        /// Occurs when an asynchronous request for <see cref="GetAllIssueCommentsAsync"/> operation completes.
        /// </summary>
        public event GetAllFeedCompletedEventHandler<IssueCommentsFeed, IssueCommentsEntry> GetAllIssueCommentsCompleted;

        /// <summary>
        /// Gets all comments for a particular issue. This method doesnot block the calling thread.
        /// </summary>
        /// <param name="issueId">Issue id</param>
        /// <param name="userState">A user-defined object that is passed to the method invoked when the asynchronous operation completes.</param>
        public void GetAllIssueCommentsAsync(string issueId, object userState)
        {
            _commentService = new ProjectHostingIssueCommentService(this);
            _commentService.GetAllIssueCommentsAsync(issueId, userState);
        }

        /// <summary>
        /// Cancels a pending asynchronous operation for <see cref="GetAllIssueCommentsAsync"/>.
        /// </summary>
        /// <param name="userState">A user-defined object to uniquely determine the asynchronous operation.</param>
        public void GetAllIssueCommentsAsyncCancel(object userState)
        {
            _commentService.GetAllIssueCommentsAsyncCancel(userState);
        }

        #endregion

        #region SubmitNewIssueComment Async

        /// <summary>
        /// Occurs when an asynchronous request for <see cref="SubmitNewIssueCommentAsync"/> operation completes.
        /// </summary>
        public event SubmitNewEntryCompletedEventHandler<IssueCommentsFeed, IssueCommentsEntry>
            SubmitNewIssueCommentCompleted;

        /// <summary>
        /// Submit new comments for a particular issue. This method does not block the calling thread.
        /// </summary>
        /// <param name="newEntry">New comment entry.</param>
        /// <param name="issueId">Id for the issue.</param>
        /// <param name="applicationName">Application which is invoking this call.</param>
        /// <param name="userState">A user-defined object that is passed to the method invoked when the asynchronous operation completes.</param>
        public void SubmitNewIssueCommentAsync(IssueCommentsEntry newEntry, string issueId, string applicationName,
                                               object userState)
        {
            _commentService = new ProjectHostingIssueCommentService(this);
            _commentService.SubmitNewIssueCommentAsync(newEntry, issueId, applicationName, userState);
        }

        /// <summary>
        /// Cancels a pending asynchronous operation for <see cref="SubmitNewIssueCommentAsync"/>.
        /// </summary>
        /// <param name="userState">A user-defined object to uniquely determine the asynchronous operation.</param>
        public void SubmitNewIssueCommentAsyncCancel(object userState)
        {
            _commentService.SubmitNewIssueCommentAsyncCancel(userState);
        }

        #endregion

        #region Nested type: DownloadData

        internal class DownloadData
        {
            internal object Token { get; set; }
            internal string DownloadPath { get; set; }
        }

        #endregion

        #region Nested type: ProjectHostingIssueCommentService

        internal partial class ProjectHostingIssueCommentService
        {
            #region GetAllIssueComments Async

            internal void GetAllIssueCommentsAsync(string issueId, object userState)
            {
                GUrl = "http://code.google.com/feeds/issues/p/" + _parentService.ProjectName + "/issues/" + issueId +
                       "/comments/full";
                GetAllFeedCompleted += ProjectHostingIssueCommentServiceGetAllFeedCompleted;
                GetAllFeedAsync(userState);
            }

            internal void GetAllIssueCommentsAsyncCancel(object userState)
            {
                GetAllFeedAsyncCancel(userState);
            }

            private void ProjectHostingIssueCommentServiceGetAllFeedCompleted(object sender,
                                                                              GetAllFeedCompletedEventArgs
                                                                                  <IssueCommentsFeed, IssueCommentsEntry
                                                                                  > e)
            {
                if (_parentService.GetAllIssueCommentsCompleted != null)
                    _parentService.GetAllIssueCommentsCompleted(sender, e);
            }

            #endregion

            #region SubmitNewIssueComment Async

            internal void SubmitNewIssueCommentAsync(IssueCommentsEntry newEntry, string issueId, string applicationName,
                                                     object userState)
            {
                if (string.IsNullOrEmpty(applicationName))
                    applicationName = "GCodeIssueTracker-1.0";

                GUrl = "http://code.google.com/feeds/issues/p/" + _parentService.ProjectName + "/issues/" + issueId +
                       "/comments/full";
                SubmitNewEntryCompleted += ProjectHostingIssueCommentServiceSubmitNewEntryCompleted;
                SubmitNewEntryAsync(newEntry, applicationName, userState);
            }

            internal void SubmitNewIssueCommentAsyncCancel(object userState)
            {
                SubmitNewEntryAsyncCancel(userState);
            }

            private void ProjectHostingIssueCommentServiceSubmitNewEntryCompleted(object sender,
                                                                                  SubmitNewEntryCompletedEventArgs
                                                                                      <IssueCommentsFeed,
                                                                                      IssueCommentsEntry> e)
            {
                if (_parentService.SubmitNewIssueCommentCompleted != null)
                    _parentService.SubmitNewIssueCommentCompleted(sender, e);
            }

            #endregion
        }

        #endregion
    }
}